package com.ecarx.vhaltests.hy11.testcases.charging;

import static com.ecarx.xui.adaptapi.car.hev.ICharging.CHARGE_FUNC_BATTERY_CHARGING_CURRENT_POWER;

import android.car.VehicleAreaType;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;

import androidx.test.platform.app.InstrumentationRegistry;

import com.ecarx.vhaltests.IpcpClient;
import com.ecarx.vhaltests.VehicleConstants;
import com.ecarx.vhaltests.VhalUnitTestBase;
import com.ecarx.xui.adaptapi.car.IWrapper;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ChargeFuncBatteryChargingCurrentPowerTest extends VhalUnitTestBase {
    private static int propertyId;
    private static IWrapper.IPropertyId wrapperProperty;
    private CarPropertyManager.CarPropertyEventCallback propertyCbk;
    private static IpcpClient ipcpClient = new IpcpClient();
    private boolean isFirstCbk;
    private static final int AREA_ID = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
    private float cbkValue;
    private final Object cbkLock = new Object();

    private static final float DEFAULT_VALUE = -1.0F;

    private static final int TYPE_DEFAULT = 1;
    private static final int TYPE_AC = 2;
    private static final int TYPE_DC = 3;

    @BeforeClass
    public static void testInit() {
        IWrapper wrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext());
        wrapperProperty = wrapper.getWrappedPropertyId(IWrapper.WrappedIdType.ID_TYPE_FUNCTION, CHARGE_FUNC_BATTERY_CHARGING_CURRENT_POWER);
        propertyId = wrapperProperty.getPropertyId();
        setAvailable(true);
        Assert.assertTrue(isAvail());
    }

    @Test
    public void test001_GetChargeFuncBatteryChargingCurrentPowerDefault() {
        testGet(TYPE_DEFAULT, DEFAULT_VALUE);
    }

    @Test
    public void test002_GetChargeFuncBatteryChargingCurrentPowerAC() {
        testGet(TYPE_AC, 4);
        testGet(TYPE_AC, 16);
    }

    @Test
    public void test003_GetChargeFuncBatteryChargingCurrentPowerDC() {
        testGet(TYPE_DC, 4);
        testGet(TYPE_DC, 16);
    }

    @Test
    public void test004_GetChargeFuncBatteryChargingCurrentPowerCbk() {
        testCbk();
    }

    @Test
    public void test005_GetChargeFuncBatteryChargingCurrentPowerUnAvail() {
        setAvailable(false);
        Assert.assertFalse(isAvail());
    }

    private static void setAvailable(boolean isAvail) {
        setAvailable(isAvail, null, null);
        delay();
    }

    private static void setAvailable(boolean isAvail, Integer type, Float expectedValue) {
        int ccSvcId = 135;
        int[] ccOperIds = {200};

        int cemSvcId = 37;
        int[] cemOperIds = {999};

        int timeoutSvcId = 99;
        int[] timeoutOperIds = {200};

        int vddmSvcId = 48;
        int[] vddmOperIds = {999};

        ipcpClient.setConfig(ccSvcId, ccOperIds);
        ipcpClient.setConfig(cemSvcId, cemOperIds);
        ipcpClient.setConfig(timeoutSvcId, timeoutOperIds);
        ipcpClient.setConfig(vddmSvcId, vddmOperIds);

        int[] ccConfigs = {13};
        int[] ccValues = {4};
        boolean ccRet = ipcpClient.setCarconfigSignal(ccSvcId, ccOperIds[0], ccConfigs, ccValues);
        Assert.assertTrue(ccRet);

        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts"};
        int[] cemValues = {isAvail ? VehicleConstants.CAR_MODE_NOMAL : VehicleConstants.CAR_MODE_FACTORY, VehicleConstants.USG_MODE_CONVENIENCE};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);

        if (type == null) {
            return;
        }

        String[] timeoutConfigs = {"onBdChrgrIActTimeout", "hvBattIDc1Timeout", "onBdChrgrUActTimeout", "hvBattUDcTimeout"};
        int[] timeoutValues = {0, 0, 0, 0};
        boolean toRet = ipcpClient.setTimeoutSignal(timeoutSvcId, timeoutOperIds[0], timeoutConfigs, timeoutValues);
        Assert.assertTrue(toRet);

        String[] vddmConfigs = {"chrgnOrDisChrgnStsFb", "onBdChrgrIAct", "hvBattIDc1", "onBdChrgrUAct", "hvBattUDc"};
        double cur = Math.sqrt(expectedValue);
        double vol = Math.sqrt(expectedValue);
        int[] vddmValues = null;
        switch (type) {
            case TYPE_DEFAULT:
                vddmValues = new int[]{1, 1, 1};
                break;
            case TYPE_AC:
                vddmValues = new int[]{5, (int) ((cur + 200) * 10), (int) ((-cur + 1638) * 10), (int) (vol * 4), 0};
                break;
            case TYPE_DC:
                vddmValues = new int[]{15, (int) ((cur + 200) * 10), (int) ((-cur + 1638) * 10), 0, (int) (vol * 4)};
                break;
        }
        if (vddmValues != null) {
            boolean retVDDM = ipcpClient.setVDDMSignal(vddmSvcId, vddmOperIds[0], vddmConfigs, vddmValues);
            Assert.assertTrue(retVDDM);
        }
    }

    private void testGet(int type, float expectedValue) {
        mockValue(type, expectedValue);
        delay();
        Assert.assertEquals(expectedValue == DEFAULT_VALUE ? expectedValue : expectedValue / 1000, getValue(), 0.01);
    }

    private void mockValue(int type, float expectedValue) {
        setAvailable(true, type, expectedValue);
        delay();
    }

    private float getValue() {
        delay();
        return mCarPropertyMgr.getFloatProperty(propertyId, AREA_ID);
    }

    private static boolean isAvail() {
        delay();
        return mCarPropertyMgr.isPropertyAvailable(propertyId, AREA_ID);
    }

    private void testCbk() {
        cbkValue = -1;
        testGet(TYPE_AC, 4);
        delay();
        float expectedValue = 16;
        propertyCbk = new CarPropertyManager.CarPropertyEventCallback() {
            @Override
            public void onChangeEvent(CarPropertyValue carPropertyValue) {
                if (carPropertyValue.getAreaId() == AREA_ID) {
                    if (isFirstCbk) {
                        isFirstCbk = false;
                    } else {
                        cbkValue = (float) carPropertyValue.getValue();
                        unLock();
                    }
                }
            }

            @Override
            public void onErrorEvent(int i, int i1) {
            }
        };
        mCarPropertyMgr.registerCallback(propertyCbk, propertyId, CarPropertyManager.SENSOR_RATE_FAST);
        delay();
        testGet(TYPE_AC, expectedValue);
        lock();
        Assert.assertEquals(expectedValue/1000, cbkValue, 0.01);
    }

    private void lock() {
        try {
            synchronized (cbkLock) {
                cbkLock.wait(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void unLock() {
        synchronized (cbkLock) {
            cbkLock.notify();
        }
    }

}